package gfx;

import java.util.HashMap;

import javax.microedition.khronos.opengles.GL10;

import com.feral.games.mathematical.R;

import util.GameVars;
import util.Logger;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;

public class Spritesheet {	

	private static int MAX_TEXTURES = 250;
	private Bitmap mSpriteSheet;
	private Bitmap mUISheet;
	private Bitmap mBackgroundSheet;
	
	private Bitmap spriteSheet;
	private Bitmap bgSheet;
	private Bitmap splashSheet;
//	private BitmapFactory.Options options; 
	
	private Bitmap fontBitmap;
	private Context mContext;
	
	Glyphs glyphs;
	
	private int[][] allTextures = new int[MAX_TEXTURES][];
	int[] glyphTextures = new int[1];
	
	HashMap<Character, int[]> glyphTextureMap = new HashMap<Character, int[]>();
	
	private boolean isLoaded = false;
	private boolean splashIsLoaded = false;
	
	private Spritesheet(){};

	private static class SpritesheetHolder {
		public static final Spritesheet instance = new Spritesheet();
	}

	public static Spritesheet getInstance(){
		return SpritesheetHolder.instance;
	}
	
	public void setContext(Context context){ mContext = context; }
	
	public int[] getTexture(int key){
		return allTextures[key];
	}
	
	public void loadAll(GL10 gl){
		loadUITextures(gl);
		loadSprites(gl);
		loadGlyphs(gl);
		isLoaded = true;
	}
	
	public void loadGlyphs(GL10 gl){
		// Load Texture
		if(fontBitmap == null)
			fontBitmap = BitmapFactory.decodeResource(mContext.getResources(),
					R.drawable.fontsheet);
//			mGlyphSheet = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.glyphs);
		
		glyphs = Glyphs.getInstance();
		glyphs.init();
		
		// Cutting up the glyphs
		// Continuing with the second row - upper cases
		// Note that the row starts at 15px - hardcoded
		for (int i = 0; i < 10; i++) {
			glyphTextureMap.put(glyphs.charactersU[i], loadTexture(gl, Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 0, Glyphs.glyphWidth, Glyphs.glyphHeight), Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		for (int i = 0; i < 10; i++) {
			glyphTextureMap.put(glyphs.charactersU[i + 10], loadTexture(gl, Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 16, Glyphs.glyphWidth, Glyphs.glyphHeight), Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		for (int i = 0; i < 10; i++) {
			glyphTextureMap.put(glyphs.charactersU[i + 20], loadTexture(gl, Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 32, Glyphs.glyphWidth, Glyphs.glyphHeight), Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		// row 3 for numbers
		for (int i = 0; i < 10; i++) {
			glyphTextureMap.put(glyphs.numbers[i], loadTexture(gl, Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 48, Glyphs.glyphWidth, Glyphs.glyphHeight), Glyphs.glyphWidth, Glyphs.glyphHeight));
		}

		// row 4 for punctuation
		for(int i = 0; i < 10; i++){
			glyphTextureMap.put(glyphs.punctuation[i], loadTexture(gl, Bitmap.createBitmap(fontBitmap, i*Glyphs.glyphWidth, 64,
					Glyphs.glyphWidth, Glyphs.glyphHeight), Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		
		// Putting it into the Glyphs class here
		
		// Continuing with the second row - upper cases
		// Note that the row starts at 15px - hardcoded
		for (int i = 0; i < 10; i++) {
			Glyphs.glyphMap.put(glyphs.charactersU[i], Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 0, Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		for (int i = 0; i < 10; i++) {
			Glyphs.glyphMap.put(glyphs.charactersU[i + 10], Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 16, Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		for (int i = 0; i < 10; i++) {
			Glyphs.glyphMap.put(glyphs.charactersU[i + 20], Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 32, Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		// row 3 for numbers
		for (int i = 0; i < 10; i++) {
			Glyphs.glyphMap.put(glyphs.numbers[i], Bitmap.createBitmap(fontBitmap,
					i * Glyphs.glyphWidth, 48, Glyphs.glyphWidth, Glyphs.glyphHeight));
		}
		
		// row 4 for punctuation
		for(int i = 0; i < 10; i++){
			Glyphs.glyphMap.put(glyphs.punctuation[i], Bitmap.createBitmap(fontBitmap, i*Glyphs.glyphWidth, 64,
					Glyphs.glyphWidth, Glyphs.glyphHeight));
		}

		
		// Generate Texture Pointer
		gl.glGenTextures(1, glyphTextures, 0);
		// Bind to Array
		gl.glBindTexture(GL10.GL_TEXTURE_2D, glyphTextures[0]);
		
		// Create Filtered Texture
		//															NEAREST = Crisp  LINEAR = Blurred
//		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
//		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);;
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

		fontBitmap.recycle();
		fontBitmap = null;
	}
	
	public void loadUITextures(GL10 gl){
//		options = new BitmapFactory.Options();
//		options.inPurgeable = true;
		
		if(mUISheet == null){
			mUISheet = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.uisheet, null);
		}
		
//		if(mFontBitmap == null){
//			mFontBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.font,options);
//		
		
//		int textureID = 0;
		
		// TODO Investigate why getting from allTextures is returning nulls ._.
		allTextures[GameVars.TEXTURE_MENU_BUTTON] = loadTexture(gl, Bitmap.createBitmap(mUISheet, 0, 0, 512, 128), 512, 128);
		allTextures[GameVars.TEXTURE_NAV_ARROW] = loadTexture(gl, Bitmap.createBitmap(mUISheet, 512, 640, 128, 256), 128, 256);
		allTextures[GameVars.TEXTURE_CONTROL_BOX] = loadTexture(gl, Bitmap.createBitmap(mUISheet, 0, 128, 1024, 512), 1024, 512);
		allTextures[GameVars.TEXTURE_CONTROL_CIRCLE] = loadTexture(gl, Bitmap.createBitmap(mUISheet, 0, 640, 512, 512), 512, 512);
//		tempTextureTest = loadTexture(gl, Bitmap.createBitmap(mUISheet, 0, 0, 512, 128), 512, 128);

		
		mUISheet.recycle();
		mUISheet = null;
	}

	public void loadBackgroundTextures(GL10 gl){
//		int backgroundImageBitmapID = 0;//TODO;
//		if(mBackgroundSheet == null){
//			mBackgroundSheet = BitmapFactory.decodeResource(mContext.getResources(), backgroundImageBitmapID, options);
//		}
//		int textureID = 0;
//		//allTextures[textureID] = loadTexture(gl, Bitmap.createBitmap(mBackgroundSheet, 0, 1536, 2048, 512), 512, 512);
//		
//		//mBackgroundSheet.recycle();
//		//mBackgroundSheet = null;
	}
	
	public void loadSprites(GL10 gl){
		if(spriteSheet == null){
			spriteSheet = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.spritesheet01, null);
		}
		
		// TODO Consider a loop for this.
		allTextures[GameVars.TEXTURE_PLAYER] = loadTexture(gl, Bitmap.createBitmap(spriteSheet, 0, 0, 64, 64), 64, 64);
		allTextures[GameVars.TEXTURE_GATE] = loadTexture(gl, Bitmap.createBitmap(spriteSheet, 0, 64, 64, 64), 64, 64);

		spriteSheet.recycle();
		spriteSheet = null;
	}
		
	public void loadSplash(GL10 gl){
		// TODO Move next square to the initializer and have it save as a variable here.
		int nextSquare = 1;
		
		for(;;){
			nextSquare *= 2;
			if(nextSquare > Camera.getInstance().SCREEN_WIDTH)
				break;
			if(nextSquare >= 1024){
				nextSquare = 1024;
				break;
			}
		}
		
		if(splashSheet == null){
			splashSheet = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.splashbgsheet, null), nextSquare * 2, nextSquare * 2, false);
		}

		System.gc();
		allTextures[GameVars.TEXTURE_SPLASH_BACKGROUND] = loadTexture(gl, Bitmap.createBitmap(splashSheet, 0, 0, nextSquare, nextSquare * 2), nextSquare, nextSquare * 2);
		
		splashSheet.recycle();
		splashSheet = null;
		
		splashIsLoaded = true;
	}
	
	public void loadBackgrounds(GL10 gl, short stageID){
		int nextSquare = 1;
		
		for(;;){
			nextSquare *= 2;
			if(nextSquare > Camera.getInstance().SCREEN_WIDTH)
				break;
			if(nextSquare >= 1024){
				nextSquare = 1024;
				break;
			}
		}
		
		if(bgSheet == null){
			switch(stageID / 100){
			case 0:
				bgSheet = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bgsheet01, null), nextSquare * 2, nextSquare * 2, false);
				break;
			default:
				bgSheet = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bgsheet01, null);
				break;
			}
		}

		System.gc();
//		allTextures[GameVars.TEXTURE_BACKGROUND_LOWER_LAYER] = loadTexture(gl, Bitmap.createBitmap(bgSheet, 0, 0, 1024, 2048), 1024, 2048);
		allTextures[GameVars.TEXTURE_BACKGROUND_LOWER_LAYER] = loadTexture(gl, Bitmap.createBitmap(bgSheet, 0, 0, nextSquare, nextSquare * 2), nextSquare, nextSquare * 2);
		System.gc();
//		allTextures[GameVars.TEXTURE_BACKGROUND_UPPER_LAYER] = loadTexture(gl, Bitmap.createBitmap(bgSheet, 1024, 0, 1024, 2048), 1024, 2048);
//		allTextures[GameVars.TEXTURE_BACKGROUND_UPPER_LAYER] = loadTexture(gl, Bitmap.createBitmap(bgSheet, nextSquare, 0, nextSquare, nextSquare * 2), nextSquare, nextSquare * 2);
//		System.gc();
		
		bgSheet.recycle();
		bgSheet = null;
	}
	
	private int[] loadTexture(GL10 gl, Bitmap bitmap, int frameWidth, int frameHeight){
		int frames = bitmap.getWidth() / frameWidth;
		int[] textures = new int[frames];
		// Generate Texture Pointer
		gl.glGenTextures(frames, textures, 0);
		for(int i = 0; i < frames; i++){
			Bitmap currentFrame = frames == 1 ? bitmap : Bitmap.createBitmap(bitmap, i*frameWidth, 0, frameWidth, frameHeight);
			// Bind to Array
			gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);
			// Create Filtered Texture													NEAREST = Crisp  LINEAR = Blurred
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
			// Specify Image for Bitmap
			GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, currentFrame, 0);
			currentFrame.recycle();
		}
		return textures;
	}
	
	public Bitmap getFontBitmap(){
		return fontBitmap;
	}
	
	public boolean isSplashLoaded(){ return splashIsLoaded; }
	public boolean isLoaded(){ return isLoaded; }
	public void setLoaded(boolean load){ isLoaded = load; }
}
